﻿Imports System.Data.Common

Public Class Recordset
    Inherits DataTable

    Private Adapter As DataAdapter
    Private RecordPointer As Integer = 0
    Private SQLText As String
    Public ErrorMessage As String = ""

    Sub New()

    End Sub

    <DebuggerHidden()> _
    Sub New(ByVal Adapter As DataAdapter)
        Me.Adapter = Adapter
        CType(Adapter, Object).Fill(Me)
    End Sub

    <DebuggerHidden()> _
    Sub New(ByVal Fields As String)
        AddFields(Fields)
    End Sub

    <DebuggerHidden()> _
    Public Sub AddNew()
        Rows.Add(NewRow)
        RecordPointer = Rows.Count - 1
    End Sub

    <DebuggerHidden()> _
    Public Sub AddNew(ByVal ParamArray Args() As Object)
        AddNew()
        SetValues(Args)
    End Sub

    <DebuggerHidden()> _
    Public Sub SetValues(ByVal ParamArray Args() As Object)
        Dim i As Integer = 1
        For Each Item As String In Args(0).Split(",")
            Field(Item.Trim) = Args(i) : i += 1
        Next
    End Sub

    <DebuggerHidden()> _
    Public Sub SetValues(ByVal Fields As String, ByVal Args As Object)
        Dim i As Integer = 0
        If TypeOf Args Is String Then Args = Args.Split(",")
        For Each Item As String In Fields.Split(",")
            Field(Item.Trim) = Args(i)
            i += 1
        Next
    End Sub

    Dim MarkedRows As New List(Of DataRow)
    <DebuggerHidden()> Public Sub MarkRow()
        MarkedRows.Add(CurrentRow)
    End Sub
    <DebuggerHidden()> Public Sub DeleteMarked()
        For Each Row As DataRow In MarkedRows
            Rows.Remove(Row)
        Next
        MarkedRows.Clear()
        MoveFirst()
    End Sub

    <DebuggerHidden()> Public Sub MoveNext()
        RecordPointer += 1
    End Sub

    <DebuggerHidden()> Public Sub MoveFirst()
        RecordPointer = 0
    End Sub

    ReadOnly Property Last() As Boolean
        <DebuggerHidden()> Get
            Return RecordPointer = Rows.Count - 1
        End Get
    End Property

    ReadOnly Property EOF() As Boolean
        <DebuggerHidden()> Get
            Return RecordPointer >= Rows.Count
        End Get
    End Property

    <DebuggerHidden()> Public Sub Save()
        CType(Adapter, Object).Update(Me)
    End Sub

    <DebuggerHidden()> Public Sub Delete()
        CurrentRow.Delete()
        CType(Adapter, Object).Update(Me)
    End Sub

    Default Property Field(ByVal FieldName As Object)
        <DebuggerHidden()> Get
            Return If(IsNumeric(FieldName), CurrentRow(CInt(FieldName)), CurrentRow(CStr(FieldName)))
        End Get
        <DebuggerHidden()> Set(ByVal Value)
            If IsNothing(Value) Then Value = DBNull.Value
            If IsNumeric(FieldName) Then CurrentRow(CInt(FieldName)) = Value Else CurrentRow(CStr(FieldName)) = Value
        End Set
    End Property

    ReadOnly Property CurrentRow() As DataRow
        <DebuggerHidden()> Get
            Return Rows(RecordPointer)
        End Get
    End Property

    ReadOnly Property NextRow() As DataRow
        <DebuggerHidden()> Get
            Return Rows(RecordPointer + 1)
        End Get
    End Property

    ReadOnly Property PrevRow() As DataRow
        <DebuggerHidden()> Get
            Return Rows(RecordPointer - 1)
        End Get
    End Property

    ReadOnly Property Empty() As Boolean
        Get
            Return Rows.Count = 0
        End Get
    End Property

    <DebuggerHidden()> Public Shadows Function [Select](ByVal Filter As String) As Recordset
        Dim Rs As Recordset = Clone()
        For Each Row As DataRow In Find(Filter)
            Rs.ImportRow(Row)
        Next
        Return Rs
    End Function

    <DebuggerHidden()> Public Function Find(ByVal Filter As String) As DataRow()
        Return MyBase.Select(Filter)
    End Function
    <DebuggerHidden()> Public Function Exists(ByVal Filter As String) As Boolean
        Return MyBase.Select(Filter).Length > 0
    End Function
    <DebuggerHidden()> Public Function Sum(ByVal Expr As String, Optional ByVal Filter As String = "")
        Return Compute("sum(" & Expr & ")", Filter)
    End Function
    <DebuggerHidden()> Public Function Avg(ByVal Expr As String, Optional ByVal Filter As String = "")
        Return Compute("avg(" & Expr & ")", Filter)
    End Function
    <DebuggerHidden()> Public Function Min(ByVal Expr As String, Optional ByVal Filter As String = "")
        Return Compute("min(" & Expr & ")", Filter)
    End Function
    <DebuggerHidden()> Public Function Max(ByVal Expr As String, Optional ByVal Filter As String = "")
        Return Compute("max(" & Expr & ")", Filter)
    End Function
    <DebuggerHidden()> Public Function Count(Optional ByVal Filter As String = "")
        Return MyBase.Select(Filter).Length
    End Function

    Function Breaks(ByVal Column As String) As Boolean
        If Last Then Return True
        If NextRow(Column) <> Field(Column) Then Return True
        Return False
    End Function

    Public Sub AddFields(ByVal Data As String)
        For Each Item As String In Data.Split(",")
            Item = Item.Trim.Replace("(", "-").Replace(")", "")
            Dim FieldInfo As Object = Item.Split("-")
            Dim Type As String = ""
            Select Case FieldInfo(1)
                Case "d" : Type = "DateTime"
                Case "i" : Type = "Int16"
                Case "m", "n" : Type = "Decimal"
                Case "s" : Type = "String"
                Case "b" : Type = "Boolean"
            End Select
            Columns.Add(New DataColumn(FieldInfo(0), System.Type.GetType("System." & Type)))
        Next
    End Sub

    Overloads Property PrimaryKey() As String
        Get
            Return ""
        End Get
        Set(ByVal Value As String)
            Dim Data = Split(Value, ",")
            Dim Array(UBound(Data)) As DataColumn
            For i As Integer = 0 To UBound(Data)
                Array(i) = Columns(Data(i))
            Next
            MyBase.PrimaryKey = Array
            CType(Adapter, Object).Fill(Me)
        End Set
    End Property

    Shadows Function ToString(ByVal FieldName As String, Optional ByVal ColSeperator As String = ",", Optional ByVal RowSeperator As String = vbCrLf) As String
        Dim Builder As New StringBuilder
        Do Until EOF
            Dim Text As String = ""
            For Each Item As String In FieldName.Split(",")
                Text &= ColSeperator & Field(Item)
            Next
            Builder.Append(RowSeperator & Mid(Text, Len(ColSeperator) + 1))
            MoveNext()
        Loop
        Return Mid(Builder.ToString, Len(RowSeperator) + 1)
    End Function

    Public Sub Transfer(ByVal Rs As Recordset, ByVal Fields As String)
        For Each FieldName As String In Split(Fields, ",")
            Field(FieldName) = Rs(FieldName)
        Next
    End Sub

	Sub Export(ByVal File As String)
		Dim Builder As New StringBuilder
		For Each Row As DataRow In Rows
			For Each Col As DataColumn In Columns
				Builder.Append(Col.ColumnName & "=" & Row(Col.ColumnName) & vbTab)
			Next
			Builder.Append(vbCrLf)
		Next
		My.Computer.FileSystem.WriteAllText(File, Builder.ToString, False, System.Text.Encoding.Default)
	End Sub

	Sub Import(ByVal File As String)
		Rows.Clear()
		For Each Line In Split(My.Computer.FileSystem.ReadAllText(File, System.Text.Encoding.Default), vbCrLf)
			If Line <> "" Then
				Me.AddNew()
				For Each Item In Line.Split(vbTab)
					If Item <> "" Then
						Dim Pos = InStr(Item, "=")
						If Columns.Contains(Mid(Item, 1, Pos - 1)) Then
							Field(Mid(Item, 1, Pos - 1)) = Mid(Item, Pos + 1)
						End If
					End If
				Next
			End If
		Next
	End Sub

End Class
